package net.cyndeline.components.item

import net.cyndeline.entitySystem.Component
import net.cyndeline.entitySystem.EntityRef
import net.cyndeline.entitySystem.Event
import scala.collection.mutable.HashMap
import scala.collection.mutable.MultiMap
import scala.collection.mutable.Set

/**
 * Provides computations needed to calculate events that should
 * be applied to the map state when a unit uses an item onto a
 * location.
 * 
 * Any item that can be used (rather than equipped) can be used
 * for various levels (higher levels give greater effect, but consumes
 * the item faster). If the item does not utilize levels, all events can
 * be found at level 0.
 */
trait ItemEventComponent extends Component {
  
  /**
   * Adds a level generator mapped to a level. Multiple generators can
   * be mapped to the same level.
   */
  def addLevel(level: Int, eventGenerator: List[EntityRef] => List[(EntityRef, List[Event])])
  
  /**
   * Which levels this item can be used for.
   */
  def eventLevels: List[Int]
  
  /**
   * Retrieves a list of events that should be sent to an entity, based on
   * a spells level and some map input (such as all entities within the
   * targeting area).
   */
  
  //TODO This should just be an ID used to retrieve the required factory from a repository
  
  def events(level: Int, mapState: List[EntityRef]): List[(EntityRef, List[Event])]
}

class ItemEvent extends ItemEventComponent {
  private val map = new HashMap[Int, Set[(List[EntityRef] => List[(EntityRef, List[Event])])]]() 
  with MultiMap[Int, (List[EntityRef] => List[(EntityRef, List[Event])])]
  
  override def addLevel(level: Int, eventGenerator: List[EntityRef] => List[(EntityRef, List[Event])]) {
    map.addBinding(level, eventGenerator)
  }
  
  override def eventLevels = map.keysIterator.toList
  
  //TODO Put in its own class and test? Lots of code.
  override def events(level: Int, mapState: List[EntityRef]): List[(EntityRef, List[Event])] = {
    val generators = map.get(level)
    
    if (generators.isDefined) {
     return generateActions(mapState, generators.get.toList)
    }
    
    Nil
  }
  
  override def copy = {
    val copy = new ItemEvent()
    
    for (key <- map.keys) {
      val generators = map.get(key)
      if (generators.isDefined) {
        for (generator <- generators.get.toList) {
          copy.addLevel(key, generator)
        }
      }
    }
    
    copy
  }
  
  private def generateActions(mapState: List[EntityRef], generatorList: List[List[EntityRef] => List[(EntityRef, List[Event])]]): List[(EntityRef, List[Event])] = {
    generatorList match {
      case head::tail => head(mapState) ::: generateActions(mapState, tail)
      case _ => Nil
    }
  }
}